测试Change Dectection

准备

先改造一下Login组件,增加一个Logout按钮

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Component({
  selector: 'app-login',
  template: `
  <a>
    <span *ngIf="needsLogin()">Login</span>
    <span *ngIf="!needsLogin()">Logout</span>
  </a>
`
})
export class LoginComponent {

  constructor(private auth: AuthService) {
  }

  needsLogin() {
    return !this.auth.isAuthenticated();
  }
}

测试类内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/* tslint:disable:no-unused-variable */
import {TestBed, async, ComponentFixture} from '@angular/core/testing';
import {LoginComponent} from './login.component';
import {AuthService} from "./auth.service";
import {DebugElement} from "@angular/core"; 
import {By} from "@angular/platform-browser"; 

describe('Component: Login', () => {

  let component: LoginComponent;
  let fixture: ComponentFixture<LoginComponent>;
  let authService: AuthService;
  let el: DebugElement; 

  beforeEach(() => {

    // refine the test module by declaring the test component
    TestBed.configureTestingModule({
      declarations: [LoginComponent],
      providers: [AuthService]
    });

    // create component and test fixture
    fixture = TestBed.createComponent(LoginComponent);

    // get test component from the fixture
    component = fixture.componentInstance;

    // UserService provided to the TestBed
    authService = TestBed.get(AuthService);

    //  get the "a" element by CSS selector (e.g., by class name)
    el = fixture.debugElement.query(By.css('a')); 
  });
});
  • 引入了DebugElement、By.
  • 定义了一个DebugElement类型的el 变量,并通过fixture获取DOM对象的引用: Login、Logout的按钮
  • 通过 el.nativeElement.textContent.trim() 我们可以获取该DOM对象的文本内容

检测内容变化

第一个测试用例

1
2
3
it('login button hidden when the user is authenticated', () => {
  expect(el.nativeElement.textContent.trim()).toBe('');
});

我们期望其内容为空,是因为Angular首次加载时并未感知内容变化。 Fixture是对测试组件的一个包装,我们可以控制它感知内容变化。 如何实现呢?答案是fixture的detectChanges方法。

1
2
3
4
5
it('login button hidden when the user is authenticated', () => {
  expect(el.nativeElement.textContent.trim()).toBe('');
  fixture.detectChanges();
  expect(el.nativeElement.textContent.trim()).toBe('Login');
});

一旦调用detchChanges方法,Angular将检测属性绑定。AuthService的authenticated方法默认返回的是false,所有应该显示Login.

现在通过spy模拟AuthService返回true:

1
2
3
4
5
6
7
it('login button hidden when the user is authenticated', () => {
  expect(el.nativeElement.textContent.trim()).toBe('');
  fixture.detectChanges();
  expect(el.nativeElement.textContent.trim()).toBe('Login');
  spyOn(authService, 'isAuthenticated').and.returnValue(true);
  expect(el.nativeElement.textContent.trim()).toBe('Login');
});

此时虽然AuthService返回true,但是没有调用detectChanges,组件的view不会更新,a标签内容依然为Login。如果我们再次调用detectChanges则可以看到内容的变化。

1
2
3
4
5
6
7
8
9
it('login button hidden when the user is authenticated', () => {
  expect(el.nativeElement.textContent.trim()).toBe('');
  fixture.detectChanges();
  expect(el.nativeElement.textContent.trim()).toBe('Login');
  spyOn(authService, 'isAuthenticated').and.returnValue(true);
  expect(el.nativeElement.textContent.trim()).toBe('Login');
  fixture.detectChanges();
  expect(el.nativeElement.textContent.trim()).toBe('Logout');
});